// This file is part of Frontier.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use std::{marker::PhantomData, sync::Arc};

// Substrate
use sc_client_api::{
	backend::{AuxStore, Backend, StorageProvider},
	UsageProvider,
};
use sc_transaction_pool::ChainApi;
use sc_transaction_pool_api::InPoolTransaction;
use sp_api::{ApiExt, ApiRef, Core, ProvideRuntimeApi};
use sp_block_builder::BlockBuilder as BlockBuilderApi;
use sp_blockchain::{ApplyExtrinsicFailed, HeaderBackend, HeaderMetadata};
use sp_inherents::{CreateInherentDataProviders, InherentData, InherentDataProvider};
use sp_runtime::{
	generic::{Digest, DigestItem},
	traits::{Block as BlockT, Header as HeaderT, One},
	TransactionOutcome,
};
use sp_timestamp::TimestampInherentData;

use crate::eth::Eth;

// babe mode 
use sp_keystore::KeystorePtr;
use sc_consensus_epochs::{
	descendent_query, EpochHeader, SharedEpochChanges, ViableEpochDescriptor,
};
use scale_codec::Encode;

const LOG_TARGET: &str = "eth-pending";

/// The generated error type for creating pending runtime api.
#[derive(Debug, thiserror::Error)]
pub(crate) enum Error {
	#[error("Failed to call runtime API, {0}")]
	CallApi(#[from] sp_api::ApiError),
	#[error("Failed to create pending inherent data, {0}")]
	PendingInherentData(#[from] sp_inherents::Error),
	#[error("Failed to create pending inherent data provider, {0}")]
	PendingCreateInherentDataProvider(#[from] Box<dyn std::error::Error + Send + Sync>),
	#[error(transparent)]
	Backend(#[from] sp_blockchain::Error),
	#[error(transparent)]
	ApplyExtrinsicFailed(#[from] ApplyExtrinsicFailed),

	// 新增的 StringError 变体
	// #[error("String error: {0}")]
	// StringError(String),  // 接受一个 String 类型的错误信息

	/// Some string error
	#[error("{0}")]
	StringError(String),

	// 新增变体来处理 sp_consensus::Error
    #[error("Consensus error: {0}")]
    ConsensusError(#[from] sp_consensus::Error), // 添加这一行

	/// Some other error.
	#[error("Other error: {0}")]
	Other(Box<dyn std::error::Error + Send + Sync>),
}

impl<B, C, P, CT, BE, A, CIDP, EC> Eth<B, C, P, CT, BE, A, CIDP, EC>
where
	B: BlockT,
	C: ProvideRuntimeApi<B>,
	C::Api: BlockBuilderApi<B>,
	C: HeaderBackend<B> + StorageProvider<B, BE> + 'static,
	BE: Backend<B>,
	A: ChainApi<Block = B>,
	CIDP: CreateInherentDataProviders<B, ()> + Send + 'static,
{
	/// Creates a pending runtime API.
	pub(crate) async fn pending_runtime_api(&self) -> Result<(B::Hash, ApiRef<C::Api>), Error> {
		let api = self.client.runtime_api();

		let info = self.client.info();
		let (best_number, best_hash) = (info.best_number, info.best_hash);

		let inherent_data_provider = self
			.pending_create_inherent_data_providers
			.create_inherent_data_providers(best_hash, ())
			.await?;
		let inherent_data = inherent_data_provider.create_inherent_data().await?;

		let digest = if let Some(digest_provider) = &self.pending_consensus_data_provider {
			if let Some(header) = self.client.header(best_hash)? {
				digest_provider.create_digest(&header, &inherent_data)?
			} else {
				Default::default()
			}
		} else {
			Default::default()
		};

		log::debug!(target: LOG_TARGET, "Pending runtime API: header digest = {digest:?}");

		let pending_header = <<B as BlockT>::Header as HeaderT>::new(
			best_number + One::one(),
			Default::default(),
			Default::default(),
			best_hash,
			digest,
		);

		// Initialize the pending block header
		api.initialize_block(best_hash, &pending_header)?;

		// Apply inherents to the pending block.
		let inherents = api.execute_in_transaction(move |api| {
			// `create_inherents` should not change any state, to ensure this we always rollback
			// the transaction.
			TransactionOutcome::Rollback(api.inherent_extrinsics(best_hash, inherent_data))
		})?;
		log::debug!(target: LOG_TARGET, "Pending runtime API: inherent len = {}", inherents.len());
		// Apply the inherents to the best block's state.
		for ext in inherents {
			let _ = api.execute_in_transaction(|api| match api.apply_extrinsic(best_hash, ext) {
				Ok(Ok(_)) => TransactionOutcome::Commit(Ok(())),
				Ok(Err(tx_validity)) => TransactionOutcome::Rollback(Err(
					ApplyExtrinsicFailed::Validity(tx_validity).into(),
				)),
				Err(err) => TransactionOutcome::Rollback(Err(Error::from(err))),
			});
		}

		// Get all extrinsics from the ready queue.
		let extrinsics: Vec<<B as BlockT>::Extrinsic> = self
			.graph
			.validated_pool()
			.ready()
			.map(|in_pool_tx| in_pool_tx.data().clone())
			.collect::<Vec<<B as BlockT>::Extrinsic>>();
		log::debug!(target: LOG_TARGET, "Pending runtime API: extrinsic len = {}", extrinsics.len());
		// Apply the extrinsics from the ready queue to the pending block's state.
		for ext in extrinsics {
			let _ = api.execute_in_transaction(|api| match api.apply_extrinsic(best_hash, ext) {
				Ok(Ok(_)) => TransactionOutcome::Commit(Ok(())),
				Ok(Err(tx_validity)) => TransactionOutcome::Rollback(Err(
					ApplyExtrinsicFailed::Validity(tx_validity).into(),
				)),
				Err(err) => TransactionOutcome::Rollback(Err(Error::from(err))),
			});
		}

		Ok((best_hash, api))
	}
}

/// Consensus data provider, pending api uses this trait object for authoring blocks valid for any runtime.
pub trait ConsensusDataProvider<B: BlockT>: Send + Sync {
	/// Attempt to create a consensus digest.
	fn create_digest(
		&self,
		parent: &B::Header,
		data: &InherentData,
	) -> Result<Digest, /*sp_inherents::Error*/Error>;
}

impl<B: BlockT> ConsensusDataProvider<B> for () {
	fn create_digest(
		&self,
		_: &B::Header,
		_: &InherentData,
	) -> Result<Digest, /*sp_inherents::Error*/Error> {
		Ok(Default::default())
	}
}

pub use self::aura::AuraConsensusDataProvider;
mod aura {
	use super::*;
	use sp_consensus_aura::{
		digests::CompatibleDigestItem,
		sr25519::{AuthorityId, AuthoritySignature},
		AuraApi, Slot, SlotDuration,
	};

	/// Consensus data provider for Aura.
	pub struct AuraConsensusDataProvider<B, C> {
		// slot duration
		slot_duration: SlotDuration,
		// phantom data for required generics
		_phantom: PhantomData<(B, C)>,
	}

	impl<B, C> AuraConsensusDataProvider<B, C>
	where
		B: BlockT,
		C: AuxStore + ProvideRuntimeApi<B> + UsageProvider<B>,
		C::Api: AuraApi<B, AuthorityId>,
	{
		/// Creates a new instance of the [`AuraConsensusDataProvider`], requires that `client`
		/// implements [`sp_consensus_aura::AuraApi`]
		pub fn new(client: Arc<C>) -> Self {
			let slot_duration = sc_consensus_aura::slot_duration(&*client)
				.expect("slot_duration is always present; qed.");
			Self {
				slot_duration,
				_phantom: PhantomData,
			}
		}
	}

	impl<B: BlockT, C: Send + Sync> ConsensusDataProvider<B> for AuraConsensusDataProvider<B, C> {
		fn create_digest(
			&self,
			_parent: &B::Header,
			data: &InherentData,
		) -> Result<Digest, /*sp_inherents::Error*/Error> {
			let timestamp = data
				.timestamp_inherent_data()?
				.expect("Timestamp is always present; qed");

			let digest_item =
				<DigestItem as CompatibleDigestItem<AuthoritySignature>>::aura_pre_digest(
					Slot::from_timestamp(timestamp, self.slot_duration),
				);

			Ok(Digest {
				logs: vec![digest_item],
			})
		}
	}
}

pub use self::babe::BabeConsensusDataProvider;
mod babe {
	use super::*;

	use sc_consensus_babe::{
		authorship, find_pre_digest, BabeIntermediate, CompatibleDigestItem, Epoch, INTERMEDIATE_KEY,
	};
	use sp_consensus_babe::{
		digests::{NextEpochDescriptor, PreDigest, SecondaryPlainPreDigest},
		inherents::BabeInherentData,
		AuthorityId, BabeApi, BabeAuthorityWeight, BabeConfiguration, ConsensusLog, BABE_ENGINE_ID,
	};
	use sp_consensus_slots::Slot;

	/// Provides BABE-compatible predigests and BlockImportParams.
	/// Intended for use with BABE runtimes.
	pub struct BabeConsensusDataProvider<B: BlockT, C, P> {
		/// shared reference to keystore
		keystore: KeystorePtr,

		/// Shared reference to the client.
		client: Arc<C>,

		/// Shared epoch changes
		epoch_changes: SharedEpochChanges<B, Epoch>,

		/// BABE config, gotten from the runtime.
		/// NOTE: This is used to fetch `slot_duration` and `epoch_length` in the
		/// `ConsensusDataProvider` implementation. Correct as far as these values
		/// are not changed during an epoch change.
		config: BabeConfiguration,

		/// Authorities to be used for this babe chain.
		authorities: Vec<(AuthorityId, BabeAuthorityWeight)>,
		_phantom: PhantomData<P>,
	}

	impl<B, C, P> BabeConsensusDataProvider<B, C, P>
	where
		B: BlockT,
		C: AuxStore
			+ HeaderBackend<B>
			+ ProvideRuntimeApi<B>
			+ HeaderMetadata<B, Error = sp_blockchain::Error>
			+ UsageProvider<B>,
		C::Api: BabeApi<B>,
	{
		pub fn new(
			client: Arc<C>,
			keystore: KeystorePtr,
			epoch_changes: SharedEpochChanges<B, Epoch>,
			authorities: Vec<(AuthorityId, BabeAuthorityWeight)>,
		) -> Result<Self, Error> {
			if authorities.is_empty() {
				return Err(Error::StringError("Cannot supply empty authority set!".into()))
			}
	
			let config = sc_consensus_babe::configuration(&*client)?;
	
			Ok(Self {
				config,
				client,
				keystore,
				epoch_changes,
				authorities,
				_phantom: Default::default(),
			})
		}

		fn epoch(&self, parent: &B::Header, slot: Slot) -> Result<Epoch, /*sp_inherents::Error*/Error> {
			let epoch_changes = self.epoch_changes.shared_data();
			let epoch_descriptor = epoch_changes
				.epoch_descriptor_for_child_of(
					descendent_query(&*self.client),
					&parent.hash(),
					*parent.number(),
					slot,
				)
				.map_err(|e| Error::StringError(format!("failed to fetch epoch_descriptor: {}", e)))?
				.ok_or(sp_consensus::Error::InvalidAuthoritiesSet)?;
	
			let epoch = epoch_changes
				.viable_epoch(&epoch_descriptor, |slot| Epoch::genesis(&self.config, slot))
				.ok_or_else(|| {
					log::info!(target: LOG_TARGET, "create_digest: no viable_epoch :(");
					sp_consensus::Error::InvalidAuthoritiesSet
				})?;
	
			Ok(epoch.as_ref().clone())
		}
	}

	impl<B, C, P> ConsensusDataProvider<B> for BabeConsensusDataProvider<B, C, P>
	where
		B: BlockT,
		C: AuxStore
			+ HeaderBackend<B>
			+ HeaderMetadata<B, Error = sp_blockchain::Error>
			+ UsageProvider<B>
			+ ProvideRuntimeApi<B>,
		C::Api: BabeApi<B>,
		P: Send + Sync,
	{
		fn create_digest(&self, parent: &B::Header, inherents: &InherentData) -> Result<Digest, /*sp_inherents::Error*/Error> {
			let slot = inherents
				.babe_inherent_data()?
				.ok_or_else(|| Error::StringError("No babe inherent data".into()))?; 

			let epoch = self.epoch(parent, slot)?;

			// this is a dev node environment, we should always be able to claim a slot.
			let logs = if let Some((predigest, _)) =
				authorship::claim_slot(slot, &epoch, &self.keystore)
			{
				vec![<DigestItem as CompatibleDigestItem>::babe_pre_digest(predigest)]
			} else {
				// well we couldn't claim a slot because this is an existing chain and we're not in the
				// authorities. we need to tell BabeBlockImport that the epoch has changed, and we put
				// ourselves in the authorities.
				let predigest =
					PreDigest::SecondaryPlain(SecondaryPlainPreDigest { slot, authority_index: 0_u32 });

				let mut epoch_changes = self.epoch_changes.shared_data();
				let epoch_descriptor = epoch_changes
					.epoch_descriptor_for_child_of(
						descendent_query(&*self.client),
						&parent.hash(),
						*parent.number(),
						slot,
					)
					.map_err(|e| {
						Error::StringError(format!("failed to fetch epoch_descriptor: {}", e))
					})?
					.ok_or(sp_consensus::Error::InvalidAuthoritiesSet)?;

				match epoch_descriptor {
					ViableEpochDescriptor::Signaled(identifier, _epoch_header) => {
						let epoch_mut = epoch_changes
							.epoch_mut(&identifier)
							.ok_or(sp_consensus::Error::InvalidAuthoritiesSet)?;

						// mutate the current epoch
						epoch_mut.authorities = self.authorities.clone();

						let next_epoch = ConsensusLog::NextEpochData(NextEpochDescriptor {
							authorities: self.authorities.clone(),
							// copy the old randomness
							randomness: epoch_mut.randomness,
						});

						vec![
							DigestItem::PreRuntime(BABE_ENGINE_ID, predigest.encode()),
							DigestItem::Consensus(BABE_ENGINE_ID, next_epoch.encode()),
						]
					},
					ViableEpochDescriptor::UnimportedGenesis(_) => {
						// since this is the genesis, secondary predigest works for now.
						vec![DigestItem::PreRuntime(BABE_ENGINE_ID, predigest.encode())]
					},
				}
			};

			Ok(Digest { logs })
		}
	}
}


